package com.hzw.saas.web.admin.hpc.controller;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.hzw.saas.api.hpc.*;
import com.hzw.saas.api.hpc.bo.*;
import com.hzw.saas.api.hpc.enums.DisplayUnit;
import com.hzw.saas.api.hpc.enums.HpcProviderType;
import com.hzw.saas.api.hpc.query.HpcAccountRenewalQuery;
import com.hzw.saas.api.hpc.query.HpcQCloudUpdateQuery;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.ExcelUtils;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.service.hpc.service.IHpcAccountSlurmService;
import com.hzw.saas.service.hpc.service.IQCloudHpcAccountService;
import com.hzw.saas.service.hpc.service.bo.SlurmAccountBO;
import com.hzw.saas.service.hpc.service.query.QCloudHpcAccountQuery;
import com.hzw.saas.web.admin.hpc.dto.HpcAccountUsageDto;
import com.hzw.saas.web.admin.hpc.param.HpcAccountBillParam;
import com.hzw.saas.web.admin.hpc.param.HpcAccountRenewalParam;
import com.hzw.saas.web.admin.hpc.param.HpcQCloudUpdateParam;
import com.hzw.saas.web.admin.hpc.param.QCloudHpcAccountParam;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author sonam
 * @sine 2021/11/26 11:31 上午
 */
@Slf4j
@Api(tags = "HPC/青云HPC账号")
@RestController
@RequiredArgsConstructor
@RequestMapping("/qcloud/hpc/account")
@Validated
public class HpcQCloudController {

    private final HttpServletResponse response;

    private final IHpcAccountSlurmService slurmService;
    private final IHpcAccountService hpcAccountService;
    private final IQCloudHpcAccountService qcHpcAccountService;
    private final IHpcAccountUserService hpcAccountUserService;
    private final IHpcAccountSearchService hpcAccountSearchService;
    private final IHpcAccountRecordService hpcAccountRecordService;
    private final IHpcAccountDetailService hpcAccountDetailService;

    @GetMapping("/slurm")
    @ApiOperationSort(value = 1)
    @ApiOperation(value = "获取 slurm 账号列表")
    public ResponseEntity<List<SlurmAccountBO>> getSlurmAccountList() {
        return ResponseEntity.ok(slurmService.getSlurmAccounts());
    }

    @Deprecated
    @PostMapping("/")
    @ApiOperationSort(value = 2)
    @ApiOperation(value = "创建青云HPC账号并绑定")
    @SysLog(mode = 3, operation = "创建青云HPC账号并绑定")
    public ResponseEntity<Void> createQCloudHpcAccount(@Valid @RequestBody QCloudHpcAccountParam hpcAccountParam) {
        qcHpcAccountService.createAndBoundQCloudAccount(MapperUtil.nf().map(hpcAccountParam, QCloudHpcAccountQuery.class), hpcAccountParam.getUserId(), hpcAccountParam.getExpireTime());
        return ResponseEntity.ok().build();
    }

    @GetMapping("/")
    @ApiOperationSort(3)
    @SysLog(operation = "查询青云HPC账号列表")
    @ApiOperation(value = "查询青云HPC账号列表", notes = "查询青云HPC账号列表")
    public ResponseEntity<IPage<HpcAccountInfoContractFullBO>> searchHpcAccount(PageParam pageParam,
                                                                                @RequestParam(defaultValue = "2") @ApiParam(value = "查询内容,1-未分配,2-已分配,3-已冻结,4-需要补充") Integer type,
                                                                                @RequestParam(required = false) @ApiParam(value = "查询关键字，1|4 关键字为hpc账号名称，2|3 hpc账号名称，客户姓名，手机号码，邮箱") String keyWord,
                                                                                @RequestParam(required = false) @ApiParam(value = "队列类型") List<String> slurmAccounts,
                                                                                @RequestParam(required = false) @ApiParam(value = "过期时间排序,0-最晚，1-最早") Integer expireSort,
                                                                                @RequestParam(required = false) @ApiParam(value = "创建时间排序,0-最晚，1-最早") Integer createSort,
                                                                                @RequestParam(required = false) @ApiParam(value = "等级ID") List<String> levelIds) {
        List<SlurmAccountBO> slurmAccountBOS = slurmService.getSlurmAccounts();
        Map<String, List<SlurmAccountBO>> slurmAccountMap = slurmAccountBOS.stream().collect(Collectors.groupingBy(SlurmAccountBO::getSlurmAccount));

        // 设置slurm队列类型
        IPage<HpcAccountInfoContractFullBO> contractFullBOIPage = hpcAccountSearchService.pageHpcAccountInfoContractFull(pageParam, keyWord, type, levelIds, null, slurmAccounts, expireSort, createSort, HpcProviderType.QY);
        contractFullBOIPage.getRecords().forEach(hpcAccountInfoContractFullBO -> {
            List<SlurmAccountBO> slurmAccountDTOS1 = slurmAccountMap.get(hpcAccountInfoContractFullBO.getSlurmAccount());
            if (CollectionUtils.isNotEmpty(slurmAccountDTOS1)) {
                hpcAccountInfoContractFullBO.setSlurmAccountDesc(slurmAccountDTOS1.get(0).getDesc());
            }
        });

        return ResponseEntity.ok(contractFullBOIPage);
    }

    @SneakyThrows
    @ApiOperationSort(4)
    @GetMapping("/export")
    @SysLog(operation = "导出青云HPC账号列表")
    @ApiOperation(value = "导出青云HPC账号列表", notes = "导出青云HPC账号列表")
    public ResponseEntity<Void> exportHpcAccount(@RequestParam(defaultValue = "2") @ApiParam(value = "查询内容,1-未分配,2-已分配,3-已冻结,4-需要补充") Integer type,
                                                 @RequestParam(required = false) @ApiParam(value = "查询关键字，1|4 关键字为hpc账号名称，2|3 hpc账号名称，客户姓名，手机号码，邮箱") String keyWord,
                                                 @RequestParam(required = false) @ApiParam(value = "队列类型") List<String> slurmAccounts,
                                                 @RequestParam(required = false) @ApiParam(value = "过期时间排序,0-最晚，1-最早") Integer expireSort,
                                                 @RequestParam(required = false) @ApiParam(value = "创建时间排序,0-最晚，1-最早") Integer createSort,
                                                 @RequestParam(required = false) @ApiParam(value = "等级ID") List<String> levelIds,
                                                 @RequestParam(required = false) @ApiParam(value = "HPC-USER-ID") List<String> hpcAccountUserIds, HttpServletResponse response) {
        IPage<HpcAccountInfoContractFullBO> pageData = hpcAccountSearchService.pageHpcAccountInfoContractFull(PageParam.getBigPageParam(), keyWord, type, levelIds, null, slurmAccounts, expireSort, createSort, HpcProviderType.QY);
        if (Objects.isNull(pageData)) {
            return ResponseEntity.ok().build();
        }
        List<HpcAccountInfoContractFullBO> hpcAccountInfoContractFullDTOS = pageData.getRecords();
        if (CollectionUtils.isNotEmpty(hpcAccountUserIds) && CollectionUtils.isNotEmpty(hpcAccountInfoContractFullDTOS)) {
            hpcAccountInfoContractFullDTOS = hpcAccountInfoContractFullDTOS.stream().filter(data -> hpcAccountUserIds.contains(data.getHpcAccountUserId())).collect(Collectors.toList());
        }
        // TODO 重写DTO
        if (CollectionUtils.isNotEmpty(hpcAccountInfoContractFullDTOS)) {
            ExcelUtils.exportExcel(hpcAccountInfoContractFullDTOS, "青云HPC账号信息", "青云HPC账号信息", HpcAccountInfoContractFullBO.class, "青云HPC账号信息", response);
        }
        return ResponseEntity.ok().build();
    }

    @ApiOperationSort(5)
    @DeleteMapping("/{hpcAccountId}")
    @SysLog(operation = "删除青云账号", mode = 3)
    @ApiOperation(value = "删除青云账号", notes = "删除青云账号，无法删除已绑定的HPC账号")
    public ResponseEntity<Void> deleteHpcAccount(@PathVariable String hpcAccountId) {
        hpcAccountService.delete(hpcAccountId);
        return ResponseEntity.ok().build();
    }

    @Deprecated
    @PostMapping("/unbound/{pid}")
    @ApiOperation(value = "解绑用户的青云HPC账号", notes = "未过期不能解绑，如果一定解绑请到期解绑，或者联系管理员进行解绑操作")
    @ApiOperationSort(6)
    @SysLog(operation = "解绑用户的青云HPC账号", mode = 3)
    public ResponseEntity<Void> unboundHpcAccount(@PathVariable String pid, @RequestParam(defaultValue = "true") boolean force) {
        hpcAccountUserService.unBoundHpcAccount(pid, force);
        return ResponseEntity.ok().build();
    }

    @Deprecated
    @ApiOperationSort(7)
    @PutMapping("/")
    @SysLog(operation = "修改已绑定的青云HPC账号", mode = 3)
    @ApiOperation(value = "修改已绑定的青云HPC账号", notes = "修改已绑定的青云HPC账号")
    public ResponseEntity<Void> updateQCloudHpcAccount(@RequestBody @Validated HpcQCloudUpdateParam updateParam) {
        hpcAccountUserService.update(MapperUtil.nf().map(updateParam, HpcQCloudUpdateQuery.class));
        return ResponseEntity.ok().build();
    }

    @ApiOperationSort(8)
    @PutMapping("/user/renew")
    @SysLog(operation = "续费用户的青云HPC账号", mode = 3)
    @ApiOperation(value = "续费用户的青云HPC账号", notes = "指定需要续费的用户HPC账号id（pid）")
    public ResponseEntity<Void> renewingHpc(@RequestBody @Validated HpcAccountRenewalParam renewalParam) {
        hpcAccountRecordService.renewHpcAccountUser(MapperUtil.nf().map(renewalParam, HpcAccountRenewalQuery.class));
        return ResponseEntity.ok().build();
    }

    @GetMapping("/user/bill")
    @SysLog
    @ApiOperation("获取青云算力用户账单")
    public ResponseEntity<IPage<HpcAccountBillBO>> pageHpcAccountUsageBill(PageParam pageParam, HpcAccountBillParam param) {
        List<HpcAccountBillBO> bills = this.listHpcAccountUsageBill(param);
        List<HpcAccountBillBO> records = ListUtil.page((int) pageParam.getCurrent() - 1, (int) pageParam.getSize(), bills);
        IPage<HpcAccountBillBO> result = new Page<>(pageParam.getCurrent(), pageParam.getSize(), bills.size());
        result.setRecords(records);
        return ResponseEntity.ok(result);
    }

    @GetMapping("/user/bill/export")
    @SysLog
    @ApiOperation("导出青云算力用户账单")
    public void exportHpcAccountUsageBill(HpcAccountBillParam param) {
        List<HpcAccountBillBO> bills = this.listHpcAccountUsageBill(param);
        try {
            String month = DateUtil.format(param.getMonth(), "yyyyMM");
            log.debug("start export hpc-account-bill excel...");
            String fileName = "hpc-account-bill-" + month;
            ExcelUtils.exportExcel(bills, "青云用户账单-" + month, "青云用户账单-" + month, HpcAccountBillBO.class, fileName, response);
        } catch (IOException e) {
            log.error("export error", e);
            throw DefaultSaasException.build("导出文件异常");
        }
    }

    @GetMapping("/user/account-list")
    @SysLog
    @ApiOperation(value = "获取指定用户所有HPC账户列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userId", value = "用户ID", paramType = "query", required = true),
    })
    public ResponseEntity<List<HpcAccountBaseBO>> listHpcAccountsByUser(@NotBlank(message = "用户ID不可为空") String userId) {
        List<HpcAccountBaseBO> result = hpcAccountRecordService.listHpcAccountsByUser(userId, HpcProviderType.QY.value());
        return ResponseEntity.ok(result);
    }

    @GetMapping("/user/bill/details")
    @SysLog
    @ApiOperation(value = "获取指定HPC账户算力使用统计")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountRecordId", value = "hpc账户记录ID", paramType = "query", required = true),
        @ApiImplicitParam(name = "month", value = "查询月份，默认为当月，yyyy-MM", paramType = "query"),
    })
    public ResponseEntity<List<HpcAccountUsageDto>> listHpcAccountUsageBillDetail(@NotBlank(message = "hpc账户记录ID不可为空") String hpcAccountRecordId,
                                                                                  @DateTimeFormat(pattern = "yyyy-MM") @JsonFormat(pattern = "yyyy-MM") Date month) {
        if (month == null) {
            month = new Date();
        }
        Date start = DateUtil.beginOfMonth(month);
        Date end = DateUtil.endOfMonth(month);

        List<HpcAccountUsageBo> hpcAccountUsageBos = hpcAccountDetailService.listHpcAccountUsage(ListUtil.toList(hpcAccountRecordId), start, end, DisplayUnit.D);

        List<HpcAccountUsageDto> hpcAccountUsageDtoList = new ArrayList<>();
        hpcAccountUsageBos.forEach(hpcAccountUsageBo -> {
            HpcAccountUsageDto hpcAccountUsageDto = MapperUtil.nf().map(hpcAccountUsageBo, HpcAccountUsageDto.class);
            Date date = hpcAccountUsageBo.getStartDate();
            hpcAccountUsageDto.setDate(DateUtil.formatDate(date));
            hpcAccountUsageDtoList.add(hpcAccountUsageDto);
        });

        // 核时保留2位小数并且四舍五入
        hpcAccountUsageDtoList.forEach(hpcAccountUsageDto -> {
            hpcAccountUsageDto.setUsage(NumberUtil.round(hpcAccountUsageDto.getUsage(), 2).doubleValue());
        });

        // 按日期升序
        hpcAccountUsageDtoList.sort((a, b) -> DateUtil.compare(a.getStartDate(), b.getStartDate()));

        return ResponseEntity.ok(hpcAccountUsageDtoList);
    }

    @GetMapping("/user/bill/tasks")
    @SysLog
    @ApiOperation(value = "获取指定HPC账户计算任务列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountRecordId", value = "hpc账户记录ID", paramType = "query", required = true),
        @ApiImplicitParam(name = "month", value = "查询月份，默认为当月，yyyy-MM", paramType = "query"),
    })
    public ResponseEntity<IPage<HpcAccountDetailBo>> listHpcAccountUsageDetail(PageParam pageParam,
                                                                               @NotBlank(message = "hpc账户记录ID不可为空") String hpcAccountRecordId,
                                                                               @DateTimeFormat(pattern = "yyyy-MM") @JsonFormat(pattern = "yyyy-MM") Date month) {
        if (month == null) {
            month = new Date();
        }
        Date start = DateUtil.beginOfMonth(month);
        Date end = DateUtil.endOfMonth(month);
        List<HpcAccountDetailBo> detailBos = hpcAccountDetailService.listHpcAccountBillTasks(ListUtil.toList(hpcAccountRecordId), start, end);
        List<HpcAccountDetailBo> records = ListUtil.page((int) pageParam.getCurrent() - 1, (int) pageParam.getSize(), detailBos);
        IPage<HpcAccountDetailBo> result = new Page<>(pageParam.getCurrent(), pageParam.getSize(), detailBos.size());
        result.setRecords(records);
        return ResponseEntity.ok(result);
    }

    @GetMapping("/user/bill/tasks/export")
    @SysLog
    @ApiOperation(value = "导出指定HPC账户计算任务列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountRecordId", value = "hpc账户记录ID", paramType = "query", required = true),
        @ApiImplicitParam(name = "month", value = "查询月份，默认为当月，yyyy-MM", paramType = "query"),
    })
    public void exportHpcAccountUsageDetail(@NotBlank(message = "hpc账户记录ID不可为空") String hpcAccountRecordId,
                                            @DateTimeFormat(pattern = "yyyy-MM") @JsonFormat(pattern = "yyyy-MM") Date month) {
        if (month == null) {
            month = new Date();
        }
        Date start = DateUtil.beginOfMonth(month);
        Date end = DateUtil.endOfMonth(month);
        List<HpcAccountDetailBo> detailBos = hpcAccountDetailService.listHpcAccountBillTasks(ListUtil.toList(hpcAccountRecordId), start, end);
        try {
            log.debug("start export hpc-account-tasks excel...");
            String fileName = "hpc-account-tasks";
            ExcelUtils.exportExcel(detailBos, "HPC计算任务", "HPC计算任务", HpcAccountDetailBo.class, fileName, response);
        } catch (IOException e) {
            log.error("export error", e);
            throw DefaultSaasException.build("导出文件异常");
        }
    }

    private List<HpcAccountBillBO> listHpcAccountUsageBill(HpcAccountBillParam param) {
        if (param.getMonth() == null) {
            param.setMonth(new Date());
        }
        Date startDate = DateUtil.beginOfMonth(param.getMonth());
        Date endDate = DateUtil.endOfMonth(param.getMonth());

        List<HpcAccountBillBO> bills = hpcAccountDetailService.listHpcAccountBill(HpcProviderType.QY.value(), param.getKeyword(), startDate, endDate);

        // 默认以月消耗降序排序
        if (ArrayUtil.isAllNull(param.getBillUsageSortDesc(), param.getTotalUsageSortDesc(), param.getTotalBalanceSortDesc())) {
            param.setBillUsageSortDesc(1);
        }
        Comparator<HpcAccountBillBO> comparing = null;
        // 排序优先级 totalBalanceSortDesc > totalUsageSortDesc > billUsageSortDesc
        comparing = this.addSort(comparing, param.getTotalBalanceSortDesc(), HpcAccountBillBO::getTotalBalance);
        comparing = this.addSort(comparing, param.getTotalUsageSortDesc(), HpcAccountBillBO::getTotalUsage);
        comparing = this.addSort(comparing, param.getBillUsageSortDesc(), HpcAccountBillBO::getBillPeriodUsage);

        return bills.stream().sorted(comparing).collect(Collectors.toList());
    }

    private <T, U extends Comparable<? super U>> Comparator<T> addSort(Comparator<T> comparing, Integer sort, Function<? super T, ? extends U> keyExtractor) {
        if (sort == null) {
            return comparing;
        }
        if (comparing == null) {
            comparing = sort <= 0 ? Comparator.comparing(keyExtractor) : Comparator.comparing(keyExtractor, Comparator.reverseOrder());
        } else {
            comparing = sort <= 0 ? comparing.thenComparing(keyExtractor) : Comparator.comparing(keyExtractor, Comparator.reverseOrder());
        }
        return comparing;
    }

}
